/*
 * This file is part of aion-unique <aion-unique.org>.
 *
 *  aion-unique is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  aion-unique is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with aion-unique.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aionemu.gameserver.services;

import javolution.util.FastMap;

import org.apache.log4j.Logger;

import com.aionemu.gameserver.configs.main.OptionsConfig;
import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.dataholders.GoodsListData;
import com.aionemu.gameserver.model.gameobjects.Item;
import com.aionemu.gameserver.model.gameobjects.NpcWhichNotGatherable;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.gameobjects.player.Storage;
import com.aionemu.gameserver.model.items.ItemId;
import com.aionemu.gameserver.model.templates.goods.GoodsList;
import com.aionemu.gameserver.model.templates.goods.Ingredient;
import com.aionemu.gameserver.model.templates.item.ItemTemplate;
import com.aionemu.gameserver.model.templates.npcTemplate.BuyListData;
import com.aionemu.gameserver.model.templates.npcTemplate.NpcTemplate;
import com.aionemu.gameserver.model.trade.TradeItem;
import com.aionemu.gameserver.model.trade.TradeList;

/**
 * @author ATracer, Rama
 * 
 */
public class TradeService
{
	private static final Logger			log				= Logger.getLogger(TradeService.class);

	private static final GoodsListData	goodsListData	= DataManager.GOODSLIST_DATA;

	public static boolean performBuy(Player player, TradeList tradeList)
	{
		NpcWhichNotGatherable npc = (NpcWhichNotGatherable) player.getTarget();
		if (npc == null || tradeList.getSellerObjId() != npc.getObjectId())
			return false;

		BuyListData tradeListData;
		try
		{
			tradeListData = ((NpcTemplate)npc.getObjectTemplate()).getFunctionData().getBuyListData();
		}
		catch (NullPointerException e)
		{
			return false;
		}

		Storage inventory = player.getInventory();
		int freeSlots = inventory.getLimit() - inventory.getAllItems().size() + 1;
		if (freeSlots < tradeList.size())
			return false; // TODO message

		FastMap<Integer, GoodsList.Item> allowedItems = FastMap.newInstance();
		for (int tradeTabId : tradeListData.getIds())
		{
			GoodsList goodsList = goodsListData.getGoodsListById(tradeTabId);
			if (goodsList != null && goodsList.getItemList() != null)
			{
				for (Object obj : goodsList.getItemList())
				{	GoodsList.Item item = (GoodsList.Item)obj;
					allowedItems.put(item.getProduction(), item);
				}
			}
		}
		
		for (TradeItem tradeItem : tradeList.getTradeItems())
		{
			if (tradeItem.getCount() < 0)
			{
				if (OptionsConfig.LOG_AUDIT)
					log.warn("[AUDIT] Player: " + player.getName() + " posible client hack. Trade count < 1");
				FastMap.recycle(allowedItems);
				return false;
			}
			ItemTemplate itemTemplate = DataManager.ITEM_DATA.getItemTemplate(tradeItem.getItemId());
			if (itemTemplate.getMaxStackCount() < tradeItem.getCount())
			{
				if (OptionsConfig.LOG_AUDIT)
					log.warn("[AUDIT] Player: " + player.getName() + " posible client hack. item count > MaxStackCount");
				FastMap.recycle(allowedItems);
				return false;
			}

			if (!allowedItems.containsKey(tradeItem.getItemId()))
			{
				if (OptionsConfig.LOG_AUDIT)
					log.warn("[AUDIT] Player: " + player.getName() + " posible client hack. Tade item not in GoodsList");
				FastMap.recycle(allowedItems);
				return false;
			}
		}

		int tradeModifier = tradeListData.getSellPriceRate();
		
		FastMap<Integer, Long> requiredItems = FastMap.newInstance();
		for (TradeItem tradeItem : tradeList.getTradeItems())
		{
			GoodsList.Item item = allowedItems.get(tradeItem.getItemId());
			for (Ingredient ingredient : item.getIngredient())
			{
				long allCount = 0;
				if (requiredItems.containsKey(ingredient.getItemId()))
					allCount = requiredItems.get(ingredient.getItemId());
				
				long count = ingredient.getCount()*tradeItem.getCount();
				if (ItemId.KINAH.value() == ingredient.getItemId())
					count *= tradeModifier/100;
				requiredItems.put(ingredient.getItemId(), count+allCount);
			}
		}
		
		for (FastMap.Entry<Integer, Long> e = requiredItems.head(), end = requiredItems.tail(); (e = e.getNext()) != end;)
		{
			int itemId = e.getKey();
			long count = e.getValue();
			
			if (ItemId.KINAH.value() == itemId)
			{
				if (player.getInventory().getKinahItem().getItemCount() < count)
				{
					if (OptionsConfig.LOG_AUDIT)
						log.warn("[AUDIT] Player: " + player.getName() + " posible client hack. kinah < cost");
					FastMap.recycle(allowedItems);
					return false;
				}
			}
			else if (itemId == 100)
			{
				if (player.getAbyssRank().getAp() < count)
				{
					if (OptionsConfig.LOG_AUDIT)
						log.warn("[AUDIT] Player: " + player.getName() + " posible client hack. abyss < cost");
					FastMap.recycle(allowedItems);
					return false;
				}
			}
			else
			{
				if (player.getInventory().getItemCountByItemId(itemId) < count)
				{
					if (OptionsConfig.LOG_AUDIT)
						log.warn("[AUDIT] Player: " + player.getName() + " posible client hack. item count < cost");
					FastMap.recycle(allowedItems);
					return false;
				}
			}
		}
		
		for (FastMap.Entry<Integer, Long> e = requiredItems.head(), end = requiredItems.tail(); (e = e.getNext()) != end;)
		{
			int itemId = e.getKey();
			long count = e.getValue();
			if (ItemId.KINAH.value() == itemId)
			{
				Item.decreaseKinah(player, count);
			}
			else if (itemId == 100)
			{
				player.getObjectTemplate().addAp(-count, true);
			}
			else
			{
				Item.decreaseItemCountByItemId(player, itemId, count);
			}
		}
		for (TradeItem tradeItem : tradeList.getTradeItems())
		{
			Item.addItem(player, tradeItem.getItemId(), tradeItem.getCount());
		}
		FastMap.recycle(allowedItems);
		return true;
	}

	/**
	 * 
	 * @param player
	 * @param tradeList
	 * @return true or false
	 */
	public static boolean performSellToShop(Player player, TradeList tradeList)
	{
		Storage inventory = player.getInventory();

		long kinahReward = 0;
		for (TradeItem tradeItem : tradeList.getTradeItems())
		{
			Item item = inventory.getItemByObjId(tradeItem.getItemId());
			// 1) don't allow to sell fake items;
			if (item == null)
				return false;

			if (item.getItemCount() - tradeItem.getCount() < 0)
			{
				if (OptionsConfig.LOG_AUDIT)
					log.warn("[AUDIT] Trade exploit, sell item count big: " + player.getName());
				return false;
			}
			else if (Item.removeItemFromInventory(player, item, false))
			{
				item.setItemLocation(64);
				player.getRepurchaseItemMap().put(item.getObjectId(), item);
				// TODO check retail packet here
				kinahReward += item.getItemTemplate().getPrice() * tradeItem.getCount();
			}
		}

		kinahReward = player.getPrices().getKinahForSell(kinahReward);
		Item.increaseKinah(player, kinahReward);

		return true;
	}

	/**
	 * @param player
	 * @param tradeList
	 */
	public static void performRepurchase(Player player, TradeList tradeList)
	{
		long kinah = 0;
		for (TradeItem item : tradeList.getTradeItems())
		{
			Item rItem = player.getRepurchaseItemMap().get(item.getItemId());
			if (rItem == null)
				return;
			if (rItem.getItemCount() != item.getCount())
				return;
			kinah += rItem.getItemTemplate().getPrice()*item.getCount()/5;
		}
		if (!Item.decreaseKinah(player, kinah))
			return;
		
		for (TradeItem item : tradeList.getTradeItems())
		{
			Item rItem = player.getRepurchaseItemMap().remove(item.getItemId());
			Item.addFullItem(player, player.getInventory(), rItem);
		}
	}
}
